﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.IO;

namespace Copy4Backup
{
	/// <summary>
	/// Handles the file changed event.
	/// </summary>
	public sealed class FileChangedHandler : FileWalker.IFWHandler
	{
		#region Fields

		/// <summary>
		/// Holds disk list.
		/// </summary>
		private List<Disk> disks_;

		/// <summary>
		/// Holds the current disk.
		/// </summary>
		private Disk currentDisk_;

		/// <summary>
		/// Holds the destination folder name.
		/// </summary>
		private readonly string destinationFolder_;

		/// <summary>
		/// Holds the folder filter list.
		/// </summary>
		private List<FolderFilter> folderFilters_;

		/// <summary>
		/// Holds the file filter list.
		/// </summary>
		private List<FileFilter> fileFilters_;

		public event EventHandler<FileChangedEventArgs> FileChangedEvent;

		#endregion Fields

		#region Properties

		/// <summary>
		/// Gets the disk list.
		/// </summary>
		public Collection<Disk> Disks
		{
			get
			{
				return new Collection<Disk>(disks_);
			}
		}

		/// <summary>
		/// Gets the max disk size.
		/// </summary>
		public Int64 DiskCapacity
		{
			get;
			private set;
		}
		#endregion Properties

		#region Methods

		/// <summary>
		/// Prevents a default instance of the FileChangedHandler class from being created.
		/// </summary>
		private FileChangedHandler()
		{
		}

		/// <summary>
		/// Initializes a new instance of the FileChangedHandler class.
		/// </summary>
		public FileChangedHandler(string destinationFolder, Int64 diskCapacity)
		{
			destinationFolder_ = destinationFolder;

			DiskCapacity = diskCapacity;

			disks_ = new List<Disk>();

			// Define 1st disk.
			currentDisk_ = new Disk(DiskCapacity, destinationFolder_, 1);
			disks_.Add(currentDisk_);

			folderFilters_ = new List<FolderFilter>();
			fileFilters_ = new List<FileFilter>();

		}

		/// <summary>
		/// Adds the folder filter.
		/// </summary>
		/// <param name="folderFilter">Folder filter to add.</param>
		public void AddFolderFilter(FolderFilter folderFilter)
		{
			folderFilters_.Add(folderFilter);
		}

		/// <summary>
		/// Adds the file filter.
		/// </summary>
		/// <param name="fileFilter">File filter to add.</param>
		public void AddFileFilter(FileFilter fileFilter)
		{
			fileFilters_.Add(fileFilter);
		}

		#region IFWHandler implementation
		/// <summary>
		/// Will be called when new file is found.
		/// </summary>
		/// <param name="startFolder">The start folder.</param>
		/// <param name="name">The file name</param>
		/// <returns>true, if to continue enumeration; otherwise - false</returns>
		public bool FileChanged(string startFolder, string name)
		{
			bool toProcess = true;
			// Check filters.
			foreach (FileFilter filter in fileFilters_)
			{
				toProcess = !filter.Matched(name);

				if (!toProcess)
				{
					break;
				}
			}

			if (toProcess)
			{
				FileInfo fi = new FileInfo(name);

				if ((fi.Length + currentDisk_.Used) > currentDisk_.Capacity)
				{
					// Go to next disk.
					currentDisk_ = new Disk(DiskCapacity, destinationFolder_, currentDisk_.Number + 1);
					disks_.Add(currentDisk_);
					FolderChanged(startFolder, fi.DirectoryName);
				}

				if (null != FileChangedEvent)
				{
					FileChangedEvent(this, new FileChangedEventArgs(fi, this.currentDisk_));
				}
				
				// Build destination file path.
				int startRootFolderPos = startFolder.LastIndexOf('\\');

				if (0 <= startRootFolderPos)
				{
					int startPos = name.IndexOf(startFolder, StringComparison.CurrentCulture);
					if (0 <= startPos)
					{
						startPos += startFolder.Length;

						String destFileName = destinationFolder_
							+ "\\" + currentDisk_.DiskName
							+ "\\" + startFolder.Substring(startRootFolderPos + 1)
							+ name.Substring(startPos);

						try
						{
							File.Copy(name, destFileName);
						}

						catch (UnauthorizedAccessException excp)
						{
							System.Windows.MessageBox.Show(String.Format("The caller does not have the required permission.{0}Error: {1}",
								System.Environment.NewLine, excp.Message),
								"File copying", System.Windows.MessageBoxButton.OK,
								System.Windows.MessageBoxImage.Error);
						}

						catch (ArgumentException excp)
						{
							System.Windows.MessageBox.Show(String.Format("The source file name (\"{0}\") or destination file name (\"{1}\") is not valid.{2}Error: {3}",
								name.ToString(), destFileName.ToString(), System.Environment.NewLine, excp.Message),
								"File copying", System.Windows.MessageBoxButton.OK,
								System.Windows.MessageBoxImage.Error);
						}

						catch (PathTooLongException excp)
						{
							System.Windows.MessageBox.Show(String.Format("The source file name (\"{0}\") or destination file name (\"{1}\") is too long.{2}Error: {3}",
								name.ToString(), destFileName.ToString(), System.Environment.NewLine, excp.Message),
								"File copying", System.Windows.MessageBoxButton.OK,
								System.Windows.MessageBoxImage.Error);
						}

						catch (DirectoryNotFoundException excp)
						{
							System.Windows.MessageBox.Show(String.Format("The source file name (\"{0}\") or destination file name (\"{1}\") is not valid.{2}Error: {3}",
								name.ToString(), destFileName.ToString(), System.Environment.NewLine, excp.Message),
								"File copying", System.Windows.MessageBoxButton.OK,
								System.Windows.MessageBoxImage.Error);
						}

						catch (FileNotFoundException excp)
						{
							System.Windows.MessageBox.Show(String.Format("The source file name (\"{0}\") is not valid.{1}Error: {2}",
								name.ToString(), System.Environment.NewLine, excp.Message),
								"File copying", System.Windows.MessageBoxButton.OK,
								System.Windows.MessageBoxImage.Error);
						}

						catch (IOException excp)
						{
							System.Windows.MessageBox.Show(String.Format("The destination file (\"{0}\") exists or I/O error.{1}Error: {2}",
								name.ToString(), System.Environment.NewLine, excp.Message),
								"File copying", System.Windows.MessageBoxButton.OK,
								System.Windows.MessageBoxImage.Error);
						}

						catch (NotSupportedException excp)
						{
							System.Windows.MessageBox.Show(String.Format("The source file name (\"{0}\") or destination file name (\"{1}\") is not valid.{2}Error: {3}",
								name.ToString(), destFileName.ToString(), System.Environment.NewLine, excp.Message),
								"File copying", System.Windows.MessageBoxButton.OK,
								System.Windows.MessageBoxImage.Error);
						}

						// Increase used size.
						currentDisk_.Used = currentDisk_.Used + fi.Length;
					}
					else
					{
						System.Diagnostics.Trace.Assert(false, "The source file path doesn't match to start folder");
					}
				}
				else
				{
					System.Diagnostics.Trace.Assert(false, "The start folder doesn't contain subfolder.");
				}
				
			}

			return true;	//To continue.
		}

		/// <summary>
		/// Will be called when new folder is found.
		/// </summary>
		/// <param name="startFolder">The start folder.</param>
		/// <param name="name">The folder name</param>
		/// <returns>true, if to continue enumeration; otherwise - false</returns>
		public bool FolderChanged(string startFolder, string name)
		{
			bool toContinue = true;

			foreach (FolderFilter filter in folderFilters_)
			{
				toContinue = !filter.Matched(name);
				if (!toContinue)
				{
					break;
				}
			}

			if (toContinue)
			{
				// Create the destination folder.
				// Build destination folder path.
				int startRootFolderPos = startFolder.LastIndexOf('\\');

				if (0 <= startRootFolderPos)
				{
					int startPos = name.IndexOf(startFolder, StringComparison.CurrentCulture);
					if (0 <= startPos)
					{
						startPos += startFolder.Length;

						String destFolder = destinationFolder_
							+ "\\" + currentDisk_.DiskName
							+ "\\" + startFolder.Substring(startRootFolderPos + 1)
							+ name.Substring(startPos);

						if (!Directory.Exists(destFolder))
						{
							try
							{
								Directory.CreateDirectory(destFolder);
							}

							catch (UnauthorizedAccessException excp)
							{
								System.Windows.MessageBox.Show(String.Format("The caller does not have the required permission.{0}Error: {1}",
									System.Environment.NewLine, excp.Message),
									"Folder creation", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);
							}

							catch (ArgumentException excp)
							{
								System.Windows.MessageBox.Show(String.Format("The directory specified by \"{0}\" is not valid.{1}Error: {2}",
									destFolder, System.Environment.NewLine, excp.Message),
									"Folder creation", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);
							}

							catch (PathTooLongException excp)
							{
								System.Windows.MessageBox.Show(String.Format("The directory specified by \"{0}\" is too long valid.{1}Error: {2}",
									destFolder, System.Environment.NewLine, excp.Message),
									"Folder creation", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);
							}

							catch (DirectoryNotFoundException excp)
							{
								System.Windows.MessageBox.Show(String.Format("The directory specified by \"{0}\" is not valid.{1}Error: {2}",
									destFolder, System.Environment.NewLine, excp.Message),
									"Folder creation", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);
							}

							catch (NotSupportedException excp)
							{
								System.Windows.MessageBox.Show(String.Format("The directory specified by \"{0}\" contains a colon character (:) that is not part of a drive label (\"C:\\\")..{1}Error: {2}",
									destFolder, System.Environment.NewLine, excp.Message),
									"Folder creation", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);
							}

							catch (IOException excp)
							{
								System.Windows.MessageBox.Show(String.Format("The directory specified by \"{0}\" is read-only.{1}Error: {2}",
									destFolder, System.Environment.NewLine, excp.Message),
									"Folder creation", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);
							}

						}
					}
					else
					{
						System.Diagnostics.Trace.Assert(false, "The source file path doesn't match to start folder");
					}
				}
				else
				{
					System.Diagnostics.Trace.Assert(false, "The start folder doesn't contain subfolder.");
				}

			}
			return toContinue;
		}
		#endregion IFWHandler implementation

		#endregion Methods
	}
}
